#include "foobar2000.h"

void stream_writer_chunk::write(const void * p_buffer,t_size p_bytes,abort_callback & p_abort)
{
    t_size remaining = p_bytes, written = 0;
    while (remaining > 0)
    {
        t_size delta = sizeof(m_buffer) - m_buffer_state;
        if (delta > remaining) delta = remaining;
        memcpy(m_buffer,(const t_uint8*)p_buffer + written,delta);
        written += delta;
        remaining -= delta;

        if (m_buffer_state == sizeof(m_buffer))
        {
            m_writer->write_lendian_t((t_uint8)m_buffer_state,p_abort);
            m_writer->write_object(m_buffer,m_buffer_state,p_abort);
            m_buffer_state = 0;
        }
    }
}

void stream_writer_chunk::flush(abort_callback & p_abort)
{
    m_writer->write_lendian_t((t_uint8)m_buffer_state,p_abort);
    if (m_buffer_state > 0)
    {
        m_writer->write_object(m_buffer,m_buffer_state,p_abort);
        m_buffer_state = 0;
    }
}

/*
	stream_writer * m_writer;
	unsigned m_buffer_state;
	unsigned char m_buffer[255];
*/

t_size stream_reader_chunk::read(void * p_buffer,t_size p_bytes,abort_callback & p_abort)
{
    t_size todo = p_bytes, done = 0;
    while (todo > 0)
    {
        if (m_buffer_size == m_buffer_state)
        {
            if (m_eof) break;
            t_uint8 temp;
            m_reader->read_lendian_t(temp,p_abort);
            m_buffer_size = temp;
            if (temp != sizeof(m_buffer)) m_eof = true;
            m_buffer_state = 0;
            if (m_buffer_size>0)
            {
                m_reader->read_object(m_buffer,m_buffer_size,p_abort);
            }
        }


        t_size delta = m_buffer_size - m_buffer_state;
        if (delta > todo) delta = todo;
        if (delta > 0)
        {
            memcpy((unsigned char*)p_buffer + done,m_buffer + m_buffer_state,delta);
            todo -= delta;
            done += delta;
            m_buffer_state += delta;
        }
    }
    return done;
}

void stream_reader_chunk::flush(abort_callback & p_abort)
{
    while (!m_eof)
    {
        p_abort.check_e();
        t_uint8 temp;
        m_reader->read_lendian_t(temp,p_abort);
        m_buffer_size = temp;
        if (temp != sizeof(m_buffer)) m_eof = true;
        m_buffer_state = 0;
        if (m_buffer_size>0)
        {
            m_reader->skip_object(m_buffer_size,p_abort);
        }
    }
}

/*
	stream_reader * m_reader;
	unsigned m_buffer_state, m_buffer_size;
	bool m_eof;
	unsigned char m_buffer[255];
*/

void stream_reader_chunk::g_skip(stream_reader * p_stream,abort_callback & p_abort)
{
    stream_reader_chunk(p_stream).flush(p_abort);
}

t_size reader_membuffer_base::read(void * p_buffer,t_size p_bytes,abort_callback & p_abort)
{
    p_abort.check_e();
    t_size max = get_buffer_size();
    if (max < m_offset) throw pfc::exception_bug_check_v2();
    max -= m_offset;
    t_size delta = p_bytes;
    if (delta > max) delta = max;
    memcpy(p_buffer,(char*)get_buffer() + m_offset,delta);
    m_offset += delta;
    return delta;
}

void reader_membuffer_base::seek(t_filesize position,abort_callback & p_abort)
{
    p_abort.check_e();
    t_filesize max = get_buffer_size();
    if (position == filesize_invalid || position > max) throw exception_io_seek_out_of_range();
    m_offset = (t_size)position;
}
